import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JPanel;


public class ArrowSegment{
	
	
	private Point startPoint;
	private Point endPoint;
	private Point vertexPoint;
	private ComponentListener moveListener;
	private ArrowLine firstPart,secondPart;
	private ArrowSegment nextSegment,prevSegment;
	private Element startElement,endElement;
	private GridPanel parent;
	private Arrow arrow;
	private boolean finished;
	
	public ArrowSegment(Point startPoint, Element startElement, GridPanel parent, Arrow arrow){
		this.startPoint=startPoint;
		nextSegment=null;
		prevSegment=null;
		endPoint=null;
		firstPart=new ArrowLine(startPoint, startPoint);
		secondPart=new ArrowLine(startPoint, startPoint);
		parent.add(firstPart);
		parent.add(secondPart);
		this.startElement=startElement;
		this.parent=parent;
		this.arrow=arrow;	
		finished=false;
		
		firstPart.addMouseMotionListener(new MouseMotionListener() {
			
			@Override
			public void mouseMoved(MouseEvent e) {
				// TODO Auto-generated method stub
				partMouseMoved(e);
			}
			
			@Override
			public void mouseDragged(MouseEvent e) {
				// TODO Auto-generated method stub
				dragFirstPart(e);
			}
		});
		
		addMouseListener();
		
		secondPart.addMouseMotionListener(new MouseMotionListener() {
			
			@Override
			public void mouseMoved(MouseEvent e) {
				// TODO Auto-generated method stub
				partMouseMoved(e);
			}
			
			@Override
			public void mouseDragged(MouseEvent e) {
				// TODO Auto-generated method stub
				dragSecondPart(e);
			}
		});
		
		moveListener=new ComponentListener() {
			
			@Override
			public void componentShown(ComponentEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void componentResized(ComponentEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void componentMoved(ComponentEvent e) {
				// TODO Auto-generated method stub
				change();
				
			}
			
			@Override
			public void componentHidden(ComponentEvent e) {
				// TODO Auto-generated method stub
				
			}
		};
		if(this.startElement!=null)
			this.startElement.addComponentListener(moveListener);
	}
	
	public void setFinished(boolean state){
		finished=state;
	}
	
	private void partMouseMoved(MouseEvent e){
		if(!finished){
			ArrowLine al=(ArrowLine)(e.getComponent());
			arrow.moveEndPoint(new Point(al.getX()+e.getX(),al.getY()+e.getY()));
		}
	}
	
	private void addMouseListener(){
		MouseListener mListener=new MouseListener() {
			
			@Override
			public void mouseReleased(MouseEvent e) {
				// TODO Auto-generated method stub
				((ArrowLine)e.getComponent()).stopDrag();
			}
			
			@Override
			public void mousePressed(MouseEvent e) {
				// TODO Auto-generated method stub
				((ArrowLine)e.getComponent()).startDrag(e.getPoint());
			}
			
			@Override
			public void mouseExited(MouseEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void mouseEntered(MouseEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void mouseClicked(MouseEvent e) {
				// TODO Auto-generated method stub
				if(!finished)
					arrow.addVertex();
			}
		};
		firstPart.addMouseListener(mListener);
		secondPart.addMouseListener(mListener);
	}
	
	private void dragFirstPart(MouseEvent e){
		if(firstPart.isDragging()){
			if(firstPart.isHorizontal())
				firstPart.setLocation(firstPart.getX(),parent.round(firstPart.getY()+e.getY()-firstPart.getStartDragPoint().y)-firstPart.getHeight()/2);
			else
				firstPart.setLocation(parent.round(firstPart.getX()+e.getX()-firstPart.getStartDragPoint().x)-firstPart.getWidth()/2,firstPart.getY());
			firstPart.recalcPoints();
			if(startElement!=null){
				Point sp=firstPart.getStartPoint();
				if(firstPart.isHorizontal()){
					if(sp.y>startElement.getBottom()||sp.y<startElement.getY()){
						firstPart.setStartPoint(new Point(startElement.getCenterX(),sp.y));
						ArrowSegment segment=new ArrowSegment(new Point(startElement.getCenterX(),startElement.getCenterY()),startElement,parent,arrow);
						segment.setEndPoint(firstPart.getStartPoint());
						segment.setNextSegment(this);
						prevSegment=segment;
					}
					else{
						
					}
				}
			}
			else{
				startPoint=firstPart.getStartPoint();
				change();
			}
			secondPart.setStartPoint(firstPart.getEndPoint());
			if(prevSegment!=null)
				prevSegment.setEndPoint(firstPart.getStartPoint());
		}
	}
	
	private void dragSecondPart(MouseEvent e){
		if(secondPart.isDragging()){	
			if(secondPart.isHorizontal())
				secondPart.setLocation(secondPart.getX(),parent.round(secondPart.getY()+e.getY()-secondPart.getStartDragPoint().y)-secondPart.getHeight()/2);
			else
				secondPart.setLocation(parent.round(secondPart.getX()+e.getX()-secondPart.getStartDragPoint().x)-secondPart.getWidth()/2,secondPart.getY());
			if(endElement!=null){
				
			}
			secondPart.recalcPoints();
			firstPart.setEndPoint(secondPart.getStartPoint());
			if(nextSegment!=null)
				nextSegment.setStartPoint(secondPart.getEndPoint());
		}
	}
	
	public boolean isSingle(){
		return vertexPoint==startPoint;
	}
	
	public void setDrawArrow(boolean state,boolean bad){
		//firstPart.setDrawArrow(state);
		secondPart.setDrawArrow(state,bad);
	}
	
	public void setStartPoint(Point startPoint){
		this.startPoint=startPoint;
		change();
	}
	
	public void setEndPoint(Point endPoint){
		this.endPoint=endPoint;
		change();
	}
	
	public void setEndElement(Element endElement){
		this.endElement=endElement;
		if(endElement!=null)
			this.endElement.addComponentListener(moveListener);
		change();
	}
	
	public void setNextSegment(ArrowSegment nextSegment){
		this.nextSegment=nextSegment;
	}
	
	public ArrowSegment getNextSegment(){
		return nextSegment;
	}
	
	public void setPrevSegment(ArrowSegment prevSegment){
		this.prevSegment=prevSegment;
	}
	
	public ArrowSegment getPrevSegment(){
		return prevSegment;
	}
	
	private void change(){
		if(endPoint==null)endPoint=startPoint;
		if(startElement!=null){
			if(endElement!=null){
				connectTwoComponents();
			}
			else
				connectElementWithPoint();
		}
		else
			if(endElement!=null){
				connectPointWithElement();
			}
			else{
				endPoint=parent.round(endPoint);
				createVertex();
			}
		firstPart.setStartPoint(startPoint);
		firstPart.setEndPoint(vertexPoint);
		secondPart.setStartPoint(vertexPoint);
		secondPart.setEndPoint(endPoint);
		firstPart.repaint();
		secondPart.repaint();
	}
	
	public boolean isFirstPartHorizontal(){
		if(startPoint==vertexPoint)
			return secondPart.isHorizontal();
		else
			return firstPart.isHorizontal();
	}
	
	public boolean isSecondPartHorizontal(){
		return secondPart.isHorizontal();
	}
	
	private void connectTwoComponents(){
		if(endElement.getBottom()<startElement.getY()){
			if(endElement.getRight()<startElement.getX()){
				startPoint=new Point(startElement.getCenterX(),startElement.getY());
				endPoint=new Point(endElement.getRight(),endElement.getCenterY());
				createVertex();
			}
			else
				if(endElement.getX()>startElement.getRight()){
					startPoint=new Point(startElement.getCenterX(),startElement.getY());
					endPoint=new Point(endElement.getX(),endElement.getCenterY());
					createVertex();
				}
				else{
					int x;
					if(endElement.getRight()>startElement.getCenterX()&&endElement.getX()<startElement.getCenterX())
						x=startElement.getCenterX();
					else
						if(endElement.getRight()<startElement.getCenterX())
							x=(endElement.getRight()+startElement.getX())/2;
						else
							x=(endElement.getX()+startElement.getRight())/2;
					startPoint=new Point(x,startElement.getY());
					endPoint=new Point(x,endElement.getBottom());
					vertexPoint=startPoint;
				}						
		}
		else
			if(endElement.getY()>startElement.getBottom()){
				if(endElement.getRight()<startElement.getX()){
					startPoint=new Point(startElement.getCenterX(),startElement.getBottom());
					endPoint=new Point(endElement.getRight(),endElement.getCenterY());
					createVertex();
				}
				else
					if(endElement.getX()>startElement.getRight()){
						startPoint=new Point(startElement.getCenterX(),startElement.getBottom());
						endPoint=new Point(endElement.getX(),endElement.getCenterY());
						createVertex();
					}
					else{
						int x;
						if(endElement.getRight()>startElement.getCenterX()&&endElement.getX()<startElement.getCenterX())
							x=startElement.getCenterX();
						else
							if(endElement.getRight()<startElement.getCenterX())
								x=(endElement.getRight()+startElement.getX())/2;
							else
								x=(endElement.getX()+startElement.getRight())/2;
						startPoint=new Point(x,startElement.getBottom());
						endPoint=new Point(x,endElement.getY());
						vertexPoint=startPoint;
					}
				
			}
			else{
				if(endElement.getRight()<startElement.getX()){
					int y;
					if(endElement.getBottom()>startElement.getCenterY()&&endElement.getY()<startElement.getCenterY())
						y=startElement.getCenterY();
					else
						if(endElement.getBottom()>startElement.getCenterY())
							y=(endElement.getBottom()+startElement.getY())/2;
						else
							y=(endElement.getY()+startElement.getBottom())/2;
					startPoint=new Point(startElement.getX(),y);
					endPoint=new Point(endElement.getRight(),y);
					vertexPoint=startPoint;
				}
				if(endElement.getX()>startElement.getRight()){
					int y;
					if(endElement.getBottom()>startElement.getCenterY()&&endElement.getY()<startElement.getCenterY())
						y=startElement.getCenterY();
					else
						if(endElement.getBottom()>startElement.getCenterY())
							y=(endElement.getBottom()+startElement.getY())/2;
						else
							y=(endElement.getY()+startElement.getBottom())/2;
					startPoint=new Point(startElement.getRight(),y);
					endPoint=new Point(endElement.getX(),y);
					vertexPoint=startPoint;
				}
			}
	}
	
	private void createVertex(){
		if(startPoint.x==endPoint.x||startPoint.y==endPoint.y)
			vertexPoint=startPoint;
		else
			if(prevSegment!=null){
				if(prevSegment.isSecondPartHorizontal())
					vertexPoint=new Point(startPoint.x,endPoint.y);
				else
					vertexPoint=new Point(endPoint.x,startPoint.y);
			}
			else
				vertexPoint=new Point(startPoint.x,endPoint.y);
	}

	private void connectElementWithPoint(){
		if(startElement.getX()<=endPoint.x&&startElement.getRight()>=endPoint.x&&startElement.getY()<=endPoint.y&&startElement.getBottom()>=endPoint.y){
			endPoint=startPoint;
			vertexPoint=startPoint;
		}
		else{
			endPoint=parent.round(endPoint);
			if(endPoint.x>=startElement.getX()&&endPoint.x<=startElement.getRight()){
				if(endPoint.y>startElement.getBottom()){
					startPoint=new Point(endPoint.x,startElement.getBottom());
					
				}
				else{
					startPoint=new Point(endPoint.x,startElement.getY());
				}
				vertexPoint=startPoint;
			}
			else{
				if(endPoint.y>=startElement.getY()&&endPoint.y<=startElement.getBottom()){
					if(endPoint.x>startElement.getRight())
						startPoint=new Point(startElement.getRight(),endPoint.y);
					else
						startPoint=new Point(startElement.getX(),endPoint.y);
					createVertex();
				}
				else{
					if(endPoint.y<startElement.getY())
						startPoint=new Point(startElement.getCenterX(),startElement.getY());
					else
						startPoint=new Point(startElement.getCenterX(),startElement.getBottom());
					createVertex();
				}
			}
		}
	}

	private void connectPointWithElement(){
		if(startPoint.x>=endElement.getX()&&startPoint.x<=endElement.getRight()){
			if(startPoint.y>endElement.getBottom())
				endPoint=new Point(startPoint.x,endElement.getBottom());
			else
				endPoint=new Point(startPoint.x,endElement.getY());
			vertexPoint=startPoint;					
		}
		else{
			if(startPoint.y>=endElement.getY()&&startPoint.y<=endElement.getBottom()){
				if(startPoint.x>endElement.getRight())
					endPoint=new Point(endElement.getRight(),startPoint.y);
				else
					endPoint=new Point(endElement.getX(),startPoint.y);
				vertexPoint=startPoint;
			}
			else{
				if(prevSegment!=null){
					if(prevSegment.isSecondPartHorizontal()){
						if(startPoint.x<endElement.getX())
							endPoint=new Point(endElement.getX(),endElement.getCenterY());
						else
							endPoint=new Point(endElement.getRight(),endElement.getCenterY());
					}
					else{
						if(startPoint.y<endElement.getY())
							endPoint=new Point(endElement.getCenterX(),endElement.getY());
						else
							endPoint=new Point(endElement.getCenterX(),endElement.getBottom());
					}
				}
				else{
					if(startPoint.y<endElement.getY())
						endPoint=new Point(endElement.getCenterX(),endElement.getY());
					else
						endPoint=new Point(endElement.getCenterX(),endElement.getBottom());
				}
				createVertex();
			}
		}
	}
	
	public Point getEndPoint(){
		return endPoint;
	}
	
	public Point getStartPoint(){
		return startPoint;
	}
	
	public void remove(){
		parent.remove(firstPart);
		parent.remove(secondPart);
	}

}
